package com.atguigu.gmall.common.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.atguigu.gmall.common.entity.GmallCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * Java配置类
 *  *
 *  *     配置：交换机应答  队列应答 接收应答消息
 *  *
 *  *
 *  *     内部接口：
 *  *
 *  *     1、SSM框架课
 *  *     spring.xml
 *  *       <bean id="haha" class="包名+类名">
 *  *           <property name="" ref=></property>
 *  *           </bean>
 *  *     2、SpringBoot框架课
 *  *     @Bean
 *  *     public 类  方法名(){
 *  *         return new 类（);
 *  *     }
 *  *
 *  *     SpringIOC容器加载类的时候 执行顺序
 *  *        1、@Component  MQProducerAckConfig实例化
 *  *        2、public MQProducerAckConfig() 构造器
 *  *        3、@Autowired 注入
 *  *        4、后置处理器
 */
@Slf4j
@Component
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {

    //将Mq ProducerAckConfig的引用 设置到 RabbitTemplate 里面的ConfirmCallback
    @Autowired
    private  RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct //后置处理器 注解
    public void adb(){
        rabbitTemplate.setConfirmCallback(this);//交换机应答借口
        rabbitTemplate.setReturnCallback(this);//队列应答机接口
    }

    /**
     * 交换机应答的实现方法
     *     成功或失败都是应答的
     * @param correlationData ：关联数据   发消息的关联数据  此对象有交换机、RoutingKey 、消息
     * @param ack  ：  应答 成功或失败   ack:true 成功    ack:false 失败
     * @param cause  如果失败了 是什么原因引起的失败 如果成功了 此值为null
     */

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack){
            log.info("交换机应答成功了:cause:{}:correlationData:{}",cause, JSONObject.toJSONString(correlationData));
        }else {
            log.error("交换机应答失败了:cause:{}:correlationData:{}",cause,JSONObject.toJSONString(correlationData));
            // 重新发消息
            reTrySendMessage(correlationData);
        }
    }

    //重新发送消息
    private void reTrySendMessage(CorrelationData correlationData) {
        GmallCorrelationData gmallCorrelationData = (GmallCorrelationData) correlationData;
        //记录重新发送的次数 如：3
        int retryCount = gmallCorrelationData.getRetryCount();
        if (retryCount <3){
            gmallCorrelationData.setRetryCount(++retryCount);
            //更新 redis gmallCorrelationData
            redisTemplate.opsForHash().put("reTrySendMessage",correlationData.getId(),JSONObject.toJSONString(gmallCorrelationData));
            //重新发消息
            rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),gmallCorrelationData.getRoutingKey(),gmallCorrelationData.getMessage(),gmallCorrelationData);
            log.info("重新发送消息的次数:{}",retryCount);
        }else {
            //公司通知系统，通知公司的管理人员 转人工
            log.error("重新发送消息次数已经耗尽了,{}",JSONObject.toJSONString(gmallCorrelationData));
        }
    }

    /**
     * 队列应答的的实现方法
     *    只能失败了才会执行下面这个方法
     * @param message   消息
     * @param replyCode  失败的状态码 404
     * @param replyText  失败的消息
     * @param exchange    交换机
     * @param routingKey  关键词
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {

        Object uuid = message.getMessageProperties().getHeader("spring_returned_message_correlation");


        //从redis中取出 gmallCorrelationData对象
         String json = (String) redisTemplate.opsForHash().get("reTrySendMessage",uuid);
        GmallCorrelationData gmallCorrelationData
                = JSONObject.parseObject(json, GmallCorrelationData.class);

        //判断是否为延迟消息
        if (gmallCorrelationData.isDelay()){
            log.info("接收到了延迟消息，他是{}",JSONObject.toJSONString(gmallCorrelationData));
            return;
        }

        //重新发送消息
        reTrySendMessage(gmallCorrelationData);
    }
}



















